package com.zerro.litez.runtime.migration;


import java.util.ArrayList;
import java.util.List;

public class MigrationParser {

    TableInfo oldTable;
    TableInfo newTable;

    String tableName = null;


    List<String> sqls = new ArrayList<>();

    public MigrationParser(TableInfo oldTable, TableInfo newTable) {
        this.oldTable = oldTable;
        this.newTable = newTable;
        tableName = oldTable.getTableName();
        onParse();
    }

    private void onParse() {
        List<String> oldTableColumnSigns = oldTable.getColumnSigns();
        List<String> newTableColumnSigns = newTable.getColumnSigns();

        List<String> sameSigns = findSameSigns(oldTableColumnSigns, newTableColumnSigns);
        List<String> newSigns = findNewSigns(oldTableColumnSigns, newTableColumnSigns);
        List<String> deletedSigns = findDeletedSigns(oldTableColumnSigns, newTableColumnSigns);



        buildAlterColumnSql(sameSigns);
        buildNewColumnSql(newSigns);
        buildDeleteColumnSql(deletedSigns);

        if (!oldTable.getTableName().equals(newTable.getTableName())) {
            renameTable(oldTable.getTableName(), newTable.getTableName());
        }

    }


    private List<String> findSameSigns(List<String> oldTableSigns, List<String> newTableSigns) {
        List<String> sameSigns = new ArrayList<>();
        for (String oldSign : oldTableSigns) {
            for (String sign : newTableSigns) {
                if (oldSign.equals(sign)) {
                    sameSigns.add(sign);
                }
            }
        }
        return sameSigns;
    }

    private List<String> findNewSigns(List<String> oldTableSigns, List<String> newTableSigns) {
        List<String> newSigns = new ArrayList<>(newTableSigns);
        for (String oldSign : oldTableSigns) {
            newSigns.removeIf(oldSign::equals);
        }
        return newSigns;
    }

    private List<String> findDeletedSigns(List<String> oldTableSigns, List<String> newTableSigns) {
        List<String> deletedSigns = new ArrayList<>(oldTableSigns);
        for (String oldSign : newTableSigns) {
            deletedSigns.removeIf(oldSign::equals);
        }
        return deletedSigns;
    }


    private void renameTable(String oldName, String newName) {
        sqls.add("ALTER TABLE " + oldName + " RENAME TO " + newName);
        tableName = newName;
    }

    private void buildNewColumnSql(List<String> signs) {
        for (String sign : signs) {
            ColumnInfo columnInfo = newTable.getColumnInfo(sign);
            if (columnInfo != null) {
                String define = columnInfo.getDefine();
                if (define.contains("primary key")) {
                    onAddNewPrimaryKey(columnInfo);
                }
                sqls.add("alter table " + tableName + " add column " + define);
            }
        }

    }

    private void buildDeleteColumnSql(List<String> signs) {
        for (String sign : signs) {
            ColumnInfo columnInfo = oldTable.getColumnInfo(sign);
            if (columnInfo != null) {
                sqls.add("alter table " + tableName + " drop column " +
                        columnInfo.getName());
            }
        }
    }

    private void buildAlterColumnSql(List<String> signs) {
        for (String sign : signs) {
            ColumnInfo oldColumnInfo = oldTable.getColumnInfo(sign);
            ColumnInfo newColumnInfo = newTable.getColumnInfo(sign);

            if (oldColumnInfo != null && newColumnInfo != null) {
                String newDefine = newColumnInfo.getDefine();
                if (!oldColumnInfo.getDefine().equals(newDefine)) {
                    sqls.add("alter table " + tableName + " change " + oldColumnInfo.getName() + " " + newDefine);
                }
            }

        }

    }


    private void onAddNewPrimaryKey(ColumnInfo columnInfo) {
        throw new RuntimeException("无法添加主键列，因为它必不可少(not null)，但不知如何针对现有行进行填充。" +
                "位于 Entity: " + tableName + " column: " + columnInfo.getName() + " define: " + columnInfo.getDefine());
    }

    public List<String> getSqls() {
        return sqls;
    }
}
